#include "LCD.h"
#include "bsp.h"
#include <cc430f6137.h>

void delayus(int delay)
{
	int i;
	for(i = 0; i < delay; i++)
	{
		// Didn't know MCLK freq. so found this with o-scope.
		// creates approx. 1us delay each loop.
		__delay_cycles(1);
	}
}

void LCDinit(void)
{
	// Make all of port1 an output for LCD data
	P1DIR = 0xFF;
	// and make port2[2:0] an output for LCD control lines
	P2DIR |= (BIT0 + BIT1 + BIT2);
	
	// Put LCD in write mode, instruction register, and lower EN
	P2OUT &= ~((0x01 << LCD_ENABLE) + (0x01 << LCD_RS) + (0x01 << LCD_RW));
	
	// In the following commands, LCDwriteCommand is not used because 
	// busy flag is not available until after initialization.
	// From HD44780 datasheet, pg 45/60...
	
	// Write 0x30 and wait > 4.1ms
	P1OUT = 0x30;
	P2OUT |= (0x01 << LCD_ENABLE);
	delayus(1);
	P2OUT &= ~(0x01 << LCD_ENABLE);
	delayus(5000);
	
	// Write 0x30 and wait > 100us
	P2OUT |= (0x01 << LCD_ENABLE);
	delayus(1);
	P2OUT &= ~(0x01 << LCD_ENABLE);
	delayus(200);
	
	// Write 0x30 and wait
	P2OUT |= (0x01 << LCD_ENABLE);
	delayus(1);
	P2OUT &= ~(0x01 << LCD_ENABLE);
	delayus(200);
	
	LCDclear();																			// clear display
	LCDfunctionSet(EIGHT_BIT, TWO_ROWS, FIVEBY8);		// 2 lines
	LCDdispOnOff(DISP_ON, CURS_INVIS, NO_BLINK);		// display on
	LCDsetEntryMode(INC, NO_SHIFT);									// increment (entry mode)
}

void LCDwriteCommand(char command, char rs)
{
	// if we need RS high, do it now
	if(rs == RS_HIGH)
		P2OUT |= (0x01 << LCD_RS);
	else
		P2OUT &= ~(0x01 << LCD_RS);
	
	// put command on Port1, then pulse enable
	P1OUT = command;
	P2OUT |= (0x01 << LCD_ENABLE);
	delayus(1);
	P2OUT &= ~(0x01 << LCD_ENABLE);
	
	// turn off the RS pin
	P2OUT &= ~(0x01 << LCD_RS);
	LCDwaitBusy();
}

// Clear the display and return the cursor to the home position
void LCDclear(void)
{
	LCDwriteCommand(0x01, RS_LOW);
}

// Return the cursor to the home position
void LCDreturnHome(void)
{
	LCDwriteCommand(0x02, RS_LOW);
}

void LCDsetEntryMode(char inc, char shift)
{
	LCDwriteCommand(0x04 + inc*0x02 + shift*0x01, RS_LOW);
}

void LCDdispOnOff(char disp, char curs, char blink)
{
	LCDwriteCommand(0x08 + disp*0x04 + curs*0x02 + blink*0x01, RS_LOW);
}

void LCDshift(char disp, char right)
{
	LCDwriteCommand(0x10 + disp*0x08 + right*0x40, RS_LOW);
}

void LCDfunctionSet(char length, char lines, char font)
{
	LCDwriteCommand(0x20 + length*0x10 + lines*0x08 + font*0x04, RS_LOW);
}

void LCDsetCGaddr(char address)
{
	LCDwriteCommand(0x40 + address, RS_LOW);
}

void LCDsetDDaddr(char address)
{
	LCDwriteCommand(0x80 + address, RS_LOW);
}

void LCDwrite(int data)
{
	LCDwriteCommand(data, RS_HIGH);
}

void LCDwaitBusy(void)
{
	// Set Port1 to input and enable pulldowns
	P1DIR = 0x00;
	
	// Put LCD in read mode
	P2OUT |= (0x01 << LCD_RW);
	
	// while BF is high, wait and pulse enable
	// enable must be high to read
	P2OUT |= (0x01 << LCD_ENABLE);
	while(P1IN & (0x01 << LCD_BUSY))
	{
		P2OUT &= ~(0x01 << LCD_ENABLE);
		delayus(1);
		P2OUT |= (0x01 << LCD_ENABLE);
		delayus(1);
	}
	P2OUT &= ~(0x01 << LCD_ENABLE);
	
	// Put LCD back into write mode
	P2OUT &= ~(0x01 << LCD_RW);
	// and set Port1, but 7 back to an output when done
	P1DIR = 0xFF;
}

void LCDmoveCursor(char row, char col)
{
	switch(row)
	{
		case 0:
			LCDsetDDaddr(col);
			break;
		case 1:
			LCDsetDDaddr(col + 0x40);
			break;
		case 2:
			LCDsetDDaddr(col + 0x14);
			break;
		case 3:
			LCDsetDDaddr(col + 0x54);
			break;
		default:
			LCDsetDDaddr(0x00);
	}
}

void LCDprintString(char* string)
{
	int i = 0;
	while(string[i] != 0)
	{
		LCDwrite(string[i++]);
	}
}

void LCDprintDecimal(uint8_t number)
{
	char decBuffer[] = {0,0};
	char temp1 = number;
	char i;
	for(i=1;i<=1;i--)			//Since decBuffer only holds 2 characters
	{
		decBuffer[i] = (temp1%10) + '0'; 
		temp1 /= 10;
	}
	LCDprintString(decBuffer);
}
